package cn.wolfcode.p2p.bussness.service.impl;

import cn.wolfcode.p2p.base.domain.Account;
import cn.wolfcode.p2p.base.domain.LoginInfo;
import cn.wolfcode.p2p.base.domain.UserInfo;
import cn.wolfcode.p2p.base.exception.DisplayableException;
import cn.wolfcode.p2p.base.query.QueryObject;
import cn.wolfcode.p2p.base.service.IAccountService;
import cn.wolfcode.p2p.base.service.IUserInfoService;
import cn.wolfcode.p2p.base.util.BitStateUtil;
import cn.wolfcode.p2p.bussness.domain.*;
import cn.wolfcode.p2p.bussness.query.BidRequestQueryObject;
import cn.wolfcode.p2p.bussness.service.*;
import cn.wolfcode.p2p.bussness.util.CalculatetUtil;
import cn.wolfcode.p2p.base.util.Constants;
import cn.wolfcode.p2p.base.util.UserContext;
import cn.wolfcode.p2p.bussness.mapper.BidRequestMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

@Service
@Transactional
public class BidRequestServiceImpl implements IBidRequestService {

    @Autowired //借款对象
    private BidRequestMapper bidRequestMapper;

    @Autowired //用户
    private IUserInfoService userInfoService;

    @Autowired //用户账户
    private IAccountService accountService;

    @Autowired //账户流水记录
    private IAccountFlowService accountFlowService;

    @Autowired //审核历史
    private IBidRequestAuditHistoryService bidRequestAuditHistoryService;

    @Autowired //投标对象
    private IBidService bidService;

    @Autowired //系统账户
    private ISystemAccountService systemAccountService;

    @Autowired //系统账户流水
    private ISystemAccountFlowService systemAccountFlowService;

    @Autowired //还款对象
    private IPaymentScheduleService paymentScheduleService;

    @Autowired //还款对象计划明细
    private IPaymentScheduleDetailService paymentScheduleDetailService;

    @Autowired //体验金账户
    private IExpAccountService expAccountService;

    @Autowired //体验金流水
    private IExpAccountFlowService expAccountFlowService;

    @Override
    public int save(BidRequest bidRequest) {
        return bidRequestMapper.insert(bidRequest);
    }

    @Override
    public int update(BidRequest bidRequest) {
        int count = bidRequestMapper.updateByPrimaryKey(bidRequest);
        if (count == 0) {
            throw new DisplayableException("乐观锁异常 bidRequestId: " + bidRequest.getId());
        }
        return count;
    }

    @Override
    public BidRequest get(Long id) {
        return bidRequestMapper.selectByPrimaryKey(id);
    }

    @Override
    public PageInfo queryList(QueryObject qo) {
        PageHelper.startPage(qo.getPageNum(), qo.getPageSize());
        List list = bidRequestMapper.selectList(qo);
        return new PageInfo(list);
    }

    /**
     * 后端发标
     *
     * @param bidRequest 借款对象 --> 系统账户
     */
    @Override
    public void applyExp(BidRequest bidRequest) {
        if (
                bidRequest.getBidRequestAmount().compareTo(Constants.BORROW_AMOUNT_MIN) >= 0 //该次的借款金额 >= 系统规定的借款最小金额
                        &&
                        bidRequest.getMinBidAmount().compareTo(Constants.BID_AMOUNT_MIN) >= 0     //该次的投标金额 >= 系统规定的最小投标金额
                ) {
            //创建一个借款对象
            BidRequest br = new BidRequest();
            br.setApplyTime(new Date());//设置申请时间
            br.setBidRequestAmount(bidRequest.getBidRequestAmount());//借款金额

            br.setBidRequestState(Constants.BIDREQUEST_STATE_APPLY);//借款的状态 (待发标)

            br.setBidRequestType(Constants.BIDREQUEST_TYPE_EXP);//借款类型(体验标)

            br.setCreateUser(UserContext.getCurrent());//借款人 (当前用户)

            br.setCurrentRate(bidRequest.getCurrentRate());//借款的利率

            br.setDisableDays(bidRequest.getDisableDays());//招标天数

            br.setMinBidAmount(bidRequest.getMinBidAmount());//设置的最小的投标金额

            br.setMonthes2Return(bidRequest.getMonthes2Return());//借款周期

            br.setReturnType(Constants.RETURN_TYPE_MONTH_INTEREST_PRINCIPAL);//还款方式 按月分期

            br.setTitle(bidRequest.getTitle());//借款标题

            //计算这笔钱的总利息
            br.setTotalRewardAmount(CalculatetUtil.calTotalInterest(br.getReturnType(), br.getBidRequestAmount(),
                    br.getCurrentRate(), br.getMonthes2Return()));

            //保存数据到数据库
            this.bidRequestMapper.insert(br);
        }
    }

    /**
     * 申请发标
     *
     * @param bidRequest 发标对象
     */
    @Override
    public void apply(BidRequest bidRequest) {
        UserInfo userInfo = userInfoService.getCurrent();
        Account account = accountService.get(UserContext.getCurrent().getId());
        if (
                userInfo.isCanBorrow()  //用户是否有借款的条件(实名认证,基本信息认证,视频认证)
                        &&
                        !userInfo.isHasBidRequestApplyProcess()//用户没有正在借款的流程
                        &&
                        bidRequest.getBidRequestAmount().compareTo(Constants.BID_AMOUNT_MIN) >= 0 //该次的借款金额 >= 系统规定的最小借款金额
                        &&
                        bidRequest.getBidRequestAmount().compareTo(account.getRemainBorrowLimit()) <= 0//借款金额<=剩余信用额度
                        &&
                        bidRequest.getMinBidAmount().compareTo(Constants.BID_AMOUNT_MIN) >= 0 //该次的投标金额 >= 系统规定的投标最小金额
                        &&
                        bidRequest.getCurrentRate().compareTo(Constants.CURRENT_RATE_MIN) >= 0     //该次的借款利息 >= 系统规定的最小利息
                        &&
                        bidRequest.getCurrentRate().compareTo(Constants.CURRENT_RATE_MAX) <= 0 //该次的借款利息 <= 系统规定的最大利息
                ) {
            //创建借款对象(为什么不用传入的bidRequest保存呢,前台传入的对象只做参数的接受.)
            BidRequest br = new BidRequest();

            //设置借款对象的相关属性,并保存到数据库中(拷贝CalculateUtil计算利息)
            br.setApplyTime(new Date());//设置申请时间
            br.setBidRequestAmount(bidRequest.getBidRequestAmount());//借款金额
            br.setBidRequestState(Constants.BIDREQUEST_STATE_APPLY);//借款的状态 (申请中)
            br.setBidRequestType(Constants.BIDREQUEST_TYPE_NORMAL);//借款类型(信用标)
            br.setCreateUser(UserContext.getCurrent());//借款人 (当前用户)
            br.setCurrentRate(bidRequest.getCurrentRate());//借款的利率
            br.setDescription(bidRequest.getDescription());//借款的描述
            br.setDisableDays(bidRequest.getDisableDays());//招标天数
            br.setMinBidAmount(bidRequest.getMinBidAmount());//设置的最小的投标金额
            br.setMonthes2Return(bidRequest.getMonthes2Return());//借款周期
            br.setReturnType(Constants.RETURN_TYPE_MONTH_INTEREST_PRINCIPAL);//还款方式 按月分期
            br.setTitle(bidRequest.getTitle());//借款标题
            //借款的总利息
            br.setTotalRewardAmount(CalculatetUtil.calTotalInterest(br.getReturnType(), br.getBidRequestAmount()
                    , br.getCurrentRate(), br.getMonthes2Return()));

            this.save(br);
            //修改借款用户userInfo的状态码,添加正在借款的状态码,更新用户的userinfo对象
            userInfo.addState(BitStateUtil.HAS_BIDREQUEST_APPLY_PROCESS);
            userInfoService.update(userInfo);
        } else {
            throw new DisplayableException("非法操作");
        }
    }

    /**
     * 后台 审核发标
     *
     * @param id          申请的ID
     * @param state       审核的状态
     * @param publishTime 发布时间
     * @param remark      审核意见
     */
    @Override
    public void publishAudit(Long id, Integer state, Date publishTime, String remark) {
        BidRequest bidRequest = this.get(id);
        //如果该对象不为null  并且 申请的状态是申请待审核状态
        if (bidRequest != null && bidRequest.getBidRequestState() == Constants.BIDREQUEST_STATE_APPLY) {
            //===================================================
            /*//记录谁审核的 审核时间审核 审核的状态
            BidRequestAuditHistory brah = new BidRequestAuditHistory();
            //设置审核人
            brah.setAuditor(UserContext.getCurrent());
            //设置审核时间
            brah.setAuditTime(new Date());
            //设置审核状态
            brah.setState(state);

            //设置申请时间 申请人 审核备注
            brah.setApplier(bidRequest.getCreateUser());
            brah.setApplyTime(bidRequest.getApplyTime());
            brah.setRemark(remark);

            brah.setBidRequestId(bidRequest.getId());//关联借款对象
            brah.setAuditType(BidRequestAuditHistory.TYPE_PUBLISH);//设置审核状态 发标审核
            //保存到数据库中
            bidRequestAuditHistoryService.save(brah);*/
            createBidRequestAuditHistory(bidRequest, state, BidRequestAuditHistory.TYPE_PUBLISH, remark);
            //===================================================
            //如果审核通过
            if (state == BidRequestAuditHistory.STATE_PASS) {
                //如果publishTime为null 即是立刻发标
                if (publishTime == null) {
                    //发布时间是当前时间
                    bidRequest.setPublishTime(new Date());
                    //借款的状态 --> 招标中
                    bidRequest.setBidRequestState(Constants.BIDREQUEST_STATE_BIDDING);
                } else {
                    //如果不为空 定时发标
                    //发布时间是前台页面传过来的时间
                    bidRequest.setPublishTime(publishTime);
                    //借款的状态 --> 待发标
                    bidRequest.setBidRequestState(Constants.BIDREQUEST_STATE_PUBLISH_PENDING);
                }


                //设置招标截止时间 disableDate = publicTime + 招标的天数
                bidRequest.setDisableDate(DateUtils.addDays(bidRequest.getPublishTime(), bidRequest.getDisableDays()));
            } else {
                //如果审核拒绝
                //设置借款的状态 --> 发标审核拒绝的状态
                bidRequest.setBidRequestState(Constants.BIDREQUEST_STATE_PUBLISH_REFUSE);

                //把申请人的借款状态码移除掉
                UserInfo userInfo = userInfoService.get(bidRequest.getCreateUser().getId());
                userInfo.removeState(BitStateUtil.HAS_BIDREQUEST_APPLY_PROCESS);
                //保存到数据库中
                userInfoService.update(userInfo);
            }
            //设置风控意见 --> remark
            bidRequest.setNote(remark);
            //更新数据到数据库中
            this.update(bidRequest);
        } else {
            throw new DisplayableException("非法参数");
        }
    }

    /**
     * 查询出所有待发标的列表
     *
     * @return 所有待发标的列表
     */
    @Override
    public List queryPublishPendngBidRequests() {
        BidRequestQueryObject qo = new BidRequestQueryObject();
        //待发标
        qo.setBidRequestState(Constants.BIDREQUEST_STATE_PUBLISH_PENDING);
        return bidRequestMapper.selectList(qo);
    }

    @Override
    public List<BidRequest> queryPublishPendngBidInOneHourRequest() {
        BidRequestQueryObject qo = new BidRequestQueryObject();
        //待发标
        qo.setBidRequestState(Constants.BIDREQUEST_STATE_PUBLISH_PENDING);
        Date now = new Date();
        qo.setEndTime(DateUtils.addHours(now, 1));
        return bidRequestMapper.selectList(qo);
    }

    /**
     * 根据不同标的类型 分组查询出来的列表
     *
     * @param bidRequestType 标的类型
     * @return
     */
    @Override
    public List<BidRequest> queryIndexData(int bidRequestType) {
        BidRequestQueryObject qo = new BidRequestQueryObject();
        //招标中 还款中 已还清
        qo.setStates(new int[]{Constants.BIDREQUEST_STATE_BIDDING, Constants.BIDREQUEST_STATE_PAYING_BACK,
                Constants.BIDREQUEST_STATE_COMPLETE_PAY_BACK});
        //分组 发送到数据库中进行分组
        qo.setOrderByCondition(" order by br.bidRequestState ");
        //当前页
        qo.setPageNum(1);
        //每页显示5条数据
        qo.setPageSize(5);
        //设置标的类型
        qo.setBidRequestType(bidRequestType);
        return bidRequestMapper.selectList(qo);
    }

    /**
     * 投标操作
     *
     * @param bidRequestId 借款对象的ID
     * @param amount       投资金额
     */
    @Override
    public void bid(Long bidRequestId, BigDecimal amount) {
        BidRequest bidRequest = this.get(bidRequestId);
        //当前对象不为null 而且状态在招标中
        if (bidRequest != null && bidRequest.getBidRequestState() == Constants.BIDREQUEST_STATE_BIDDING) {
            //获取当前登录用户的账户信息
            Account account = accountService.get(UserContext.getCurrent().getId());
            //获取当前登陆用户的体验金账户
            ExpAccount expAccount = expAccountService.get(UserContext.getCurrent().getId());
            if (
                    amount.compareTo(bidRequest.getMinBidAmount()) >= 0 &&//投资金额 >= 系统设置的最小投资金额
                            ((amount.compareTo(bidRequest.getRemainAmount().min(account.getUsableAmount())) <= 0 &&// 投标金额 <= min(借款还需多少金额,当前账户的可用金额)
                                    !UserContext.getCurrent().getId().equals(bidRequest.getCreateUser().getId()) &&// 当前登录的用户不是发标用户
                                    bidRequest.getBidRequestType() == Constants.BIDREQUEST_TYPE_NORMAL //并且当前标是信用标
                            ) || (
                                    bidRequest.getBidRequestType() == Constants.BIDREQUEST_TYPE_EXP &&//当前标是体验标
                                            amount.compareTo(bidRequest.getRemainAmount().min(expAccount.getUsableAmount())) <= 0 //投标金额 <= min(借款还需多少金额,当前账户的可用金额)
                            ))
                    ) {
                //创建投标对象
                Bid bid = new Bid();
                //设置年化利率
                bid.setActualRate(bidRequest.getCurrentRate());
                //设置投标金额
                bid.setAvailableAmount(amount);
                //设置借款对象ID
                bid.setBidRequestId(bidRequest.getId());
                //设置借款标题
                bid.setBidRequestTitle(bidRequest.getTitle());
                //设置投标人 当前登录账户是投标人
                bid.setBidUser(UserContext.getCurrent());
                //设置投标时间
                bid.setBidTime(new Date());
                //设置借款状态
                bid.setBidRequestState(bidRequest.getBidRequestState());
                //保存到数据库中
                bidService.save(bid);

                //修改bidCount 的个数 currentSum当前已投得到金额
                bidRequest.setBidCount(bidRequest.getBidCount() + 1);//投标次数 + 1
                bidRequest.setCurrentSum(bidRequest.getCurrentSum()
                        .add(amount));//之前已投的金额 + 现在投的金额

                if (bidRequest.getBidRequestType() == Constants.BIDREQUEST_TYPE_NORMAL) {
                    //投资人的变化 可用金额变少 冻结金额增加
                    account.setUsableAmount(account.getUsableAmount().
                            subtract(amount));//之前的可用金额 - 支付出投标金额
                    account.setFreezedAmount(account.getFreezedAmount().
                            add(amount));//之前的冻结金额 + 支付出投标金额
                    //更新账户信息到数据库中
                    accountService.update(account);
                    //增加账户的流水的冻结金额信息
                    accountFlowService.createRechargeOfflineFlow(account, amount, "投标冻结" + amount + "元",
                            Constants.ACCOUNT_ACTIONTYPE_BID_FREEZED);
                } else {
                    //用户体验金减少 冻结体验金增加
                    expAccount.setUsableAmount(expAccount.getUsableAmount().subtract(amount));
                    expAccount.setFreezedAmount(expAccount.getFreezedAmount().add(amount));
                    //更新账户信息到数据库中
                    expAccountService.update(expAccount);
                    //增加体验金张华的冻结金流水信息
                    expAccountFlowService.createBidFlow(expAccount, amount);
                }

                //判断借款是否已经投满 借款已投金额 == 借款金额
                if (bidRequest.getBidRequestAmount().compareTo(bidRequest.getCurrentSum()) == 0) {
                    //如果拿到的类型是体验标 返回 -> 二审; 不是 -> 一审
                    int state = bidRequest.getBidRequestType() == Constants.BIDREQUEST_TYPE_EXP ? Constants.BIDREQUEST_STATE_APPROVE_PENDING_2 :
                            Constants.BIDREQUEST_STATE_APPROVE_PENDING_1;

                    //设置标的状态
                    bidRequest.setBidRequestState(state);
                    //投标的状态码 --> 满标一审
                    bidService.updateState(bidRequest.getId(), Constants.BIDREQUEST_STATE_APPROVE_PENDING_1);
                }
                this.update(bidRequest);
            } else {
                throw new DisplayableException("非法参数");
            }
        }
    }

    /**
     * @param id     满标一审对象ID
     * @param state  审核状态
     * @param remark 风控意见
     */
    @Override
    public void audit1(Long id, Integer state, String remark) {
        BidRequest bidRequest = this.get(id);
        //如果当前对象不为null 且状态为满标1审
        if (bidRequest != null && bidRequest.getBidRequestState() == Constants.BIDREQUEST_STATE_APPROVE_PENDING_1) {
            //需要创建什么对象
            createBidRequestAuditHistory(bidRequest, state, BidRequestAuditHistory.TYPE_AUDIT1, remark);
            //如果审核通过
            if (state == BidRequestAuditHistory.STATE_PASS) {
                //审核通过 把状态修改为满标二审
                bidRequest.setBidRequestState(Constants.BIDREQUEST_STATE_APPROVE_PENDING_2);

                bidService.updateState(bidRequest.getId(), Constants.BIDREQUEST_STATE_APPROVE_PENDING_2);
            } else {
                //审核拒绝抽成一个方法
                auditReject(bidRequest);
            }
        }
        //更新到数据库
        this.update(bidRequest);
    }

    private void createBidRequestAuditHistory(BidRequest bidRequest, Integer state,
                                              int typeAudit1, String remark) {
        BidRequestAuditHistory brah = new BidRequestAuditHistory();
        //设置申请人
        brah.setApplier(bidRequest.getCreateUser());
        //设置申请时间
        brah.setApplyTime(bidRequest.getApplyTime());
        //设置审核人
        brah.setAuditor(UserContext.getCurrent());
        //设置审核时间
        brah.setAuditTime(new Date());
        //设置审核备注
        brah.setRemark(remark);
        //设置审核状态
        brah.setState(state);
        //关联借款对象
        brah.setBidRequestId(bidRequest.getId());
        //设置审核类型
        brah.setAuditType(typeAudit1);
        //保存到数据库
        bidRequestAuditHistoryService.save(brah);
    }

    //审核拒绝的业务逻辑
    private void auditReject(BidRequest bidRequest) {
        //审核拒绝 把状态修改成满标拒绝状态
        bidRequest.setBidRequestState(Constants.BIDREQUEST_STATE_REJECTED);
        //对于借款对象和投标对象也是需要改变状态
        bidService.updateState(bidRequest.getId(), Constants.BIDREQUEST_STATE_REJECTED);

        //对于投资人 资金原路返回 冻结资金较少 可用资金增加
        List<Bid> bids = bidRequest.getBids();
        //如果直接遍历出来的数据都要和数据库做交互处理的话 性能上会慢很多
        //所以为了减少和数据库交互 尽量吧数据存放在map内存中 然后分类 再统一处理
        Map<Long, Account> accountMap = new HashMap<>();
        Map<Long, ExpAccount> expAccountMap = new HashMap<>();
        Account bidUserAccount;
        ExpAccount expAccount;
        Long bidUserId;
        //遍历bids集合
        for (Bid bid : bids) {
            bidUserId = bid.getBidUser().getId();
            //如果是信用标
            if (bidRequest.getBidRequestType() == Constants.BIDREQUEST_TYPE_NORMAL) {
                bidUserAccount = accountMap.get(bidUserId);
                //如果bidUserAccount为null的话 那就去数据库查询当前id的account语句
                if (bidUserAccount == null) {
                    bidUserAccount = accountService.get(bidUserId);
                    //然后存放在map内存中
                    accountMap.put(bidUserId, bidUserAccount);
                }
                //把冻结金额 减去 bid中的投标金额 得到会原来的冻结金额
                bidUserAccount.setFreezedAmount(bidUserAccount.getFreezedAmount().subtract(bid.getAvailableAmount()));
                //把现在的可用金额 +  bid中的投标金额 得到原来的可用金额
                bidUserAccount.setUsableAmount(bidUserAccount.getUsableAmount().add(bid.getAvailableAmount()));
                //设置账户流水明细记录 流水类型是 取消冻结金额 标审核失败
                accountFlowService.createRechargeOfflineFlow(bidUserAccount, bid.getAvailableAmount(), "投标失败,取消冻结:" +
                                bid.getAvailableAmount() + "元",
                        Constants.ACCOUNT_ACTIONTYPE_BID_UNFREEZED);
            } else {
                expAccount = expAccountMap.get(bidUserId);
                if (expAccount == null) {
                    expAccount = expAccountService.get(bidUserId);
                    expAccountMap.put(bidUserId, expAccount);
                }
                //把冻结金额 减去 bid中的投标金额 得到会原来的冻结金额
                expAccount.setFreezedAmount(expAccount.getFreezedAmount().subtract(bid.getAvailableAmount()));
                //把现在的可用金额 +  bid中的投标金额 得到原来的可用金额
                expAccount.setUsableAmount(expAccount.getUsableAmount().add(bid.getAvailableAmount()));
                //生成流水
                expAccountFlowService.createAudit2Flow(expAccount, bid.getAvailableAmount());
            }
        }
        //对对象统一修改
        for (Account account : accountMap.values()) {
            //更新数据到数据库中
            accountService.update(account);
        }

        //系统账户统一更新
        for (ExpAccount account : expAccountMap.values()) {
            //更新到数据库
            expAccountService.update(account);
        }

        //如果是信用标 才需要对借款人对象的状态做移除
        if (bidRequest.getBidRequestType() == Constants.BIDREQUEST_TYPE_NORMAL) {
            //对于借款人的变化
            UserInfo userInfo = userInfoService.get(bidRequest.getCreateUser().getId());
            //移除正在审核流程中的状态
            userInfo.removeState(BitStateUtil.HAS_BIDREQUEST_APPLY_PROCESS);
            //更新信息到数据库
            userInfoService.update(userInfo);
        }
    }

    @Override
    public void audit2(Long id, Integer state, String remark) {
        BidRequest bidRequest = this.get(id);
        //当前对象那个不为null 并且状态处于在满标二审
        if (bidRequest != null && bidRequest.getBidRequestState() == Constants.BIDREQUEST_STATE_APPROVE_PENDING_2) {
            //创建审核历史的记录
            createBidRequestAuditHistory(bidRequest, state, BidRequestAuditHistory.TYPE_AUDIT2, remark);
            //如果审核通过
            if (state == BidRequestAuditHistory.STATE_PASS) {
                //借款对象 与 投标对象的状态 --> 还款中
                bidRequest.setBidRequestState(Constants.BIDREQUEST_STATE_PAYING_BACK);//借款对象状态 --> 还款中
                bidService.updateState(bidRequest.getId(), Constants.BIDREQUEST_STATE_PAYING_BACK);//投标对象状态 --> 还款中

                //如果是信用贷 才需要借款人对象 体验标是不需要借款人
                if (bidRequest.getBidRequestType() == Constants.BIDREQUEST_TYPE_NORMAL) {
                    Account userAccount = accountService.get(bidRequest.getCreateUser().getId());//借款对象账户信息

                    //借款人账户 可用金额增加 待还本息增加 剩余授信额度减少
                    //可用金额增加 原本的可用资金 + 投标金额
                    userAccount.setUsableAmount(userAccount.getUsableAmount().add(bidRequest.getBidRequestAmount()));
                    //待还本息增加 待还本息 = 原待还本息 + 该次借款金额 + 该次借款的总利息
                    userAccount.setUnReturnAmount(userAccount.getUnReturnAmount().add(bidRequest.getBidRequestAmount()
                            .add(bidRequest.getTotalRewardAmount())));
                    //授信额度减少 原本的剩余授信额度 - 该次借款的金额
                    userAccount.setRemainBorrowLimit(userAccount.getRemainBorrowLimit().subtract(bidRequest.getBidRequestAmount()));

                    //生成借款成功流水 账户信息 金额的改变数 备注 流水类型
                    accountFlowService.createRechargeOfflineFlow(userAccount, bidRequest.getBidRequestAmount(), "成功借款: " +
                            bidRequest.getBidRequestAmount() + "元", Constants.ACCOUNT_ACTIONTYPE_BIDREQUEST_SUCCESSFUL);

                    //支付平台的借款手续费5%
                    BigDecimal accountManagementCharge = CalculatetUtil.calAccountManagementCharge(bidRequest.getBidRequestAmount());
                    //借款人可用金额减少
                    userAccount.setUsableAmount(userAccount.getUsableAmount().subtract(accountManagementCharge));

                    //更新借款人账户
                    accountService.update(userAccount);

                    //生成支付平台借款手续费流水
                    accountFlowService.createRechargeOfflineFlow(userAccount, accountManagementCharge, "支付平台管理费: " + accountManagementCharge + "元",
                            Constants.ACCOUNT_ACTIONTYPE_CHARGE);

                    //系统账户可用金额增加
                    SystemAccount systemAccount = systemAccountService.get();
                    //把借款手续费 存到 系统用户中 原本的可用金额 + 新增的可用金额
                    systemAccount.setUsableAmount(systemAccount.getUsableAmount().add(accountManagementCharge));
                    //成功收取用户借款手续费流水 并保存到数据库
                    systemAccountFlowService.createGainAccountManagementChargeFlow(systemAccount, accountManagementCharge);
                    //系统账户更新到数据库
                    systemAccountService.update(systemAccount);

                    //发标人的ID 移除借款码
                    UserInfo userInfo = userInfoService.get(bidRequest.getCreateUser().getId());
                    //移除借款用户的借款状态码
                    userInfo.removeState(BitStateUtil.HAS_BIDREQUEST_APPLY_PROCESS);
                    //更新到数据库
                    userInfoService.update(userInfo);
                }

                //投资人的变化
                List<Bid> bids = bidRequest.getBids();
                Long bidUserId;//投标对象的ID
                Account bidAccount;//投标对象的账户
                ExpAccount expAccount;//体验标账户对象
                Map<Long, Account> accountMap = new HashMap<>();//把相同的投标对象的ID 存放在同一个map内存中
                Map<Long, ExpAccount> expAccountMap = new HashMap<>(); //体验标账户信息
                //遍历借款对象中的投标记录
                for (Bid bid : bids) {
                    bidUserId = bid.getBidUser().getId();//获取投标人的ID

                    //如果是信用标
                    if (bidRequest.getBidRequestType() == Constants.BIDREQUEST_TYPE_NORMAL) {
                        bidAccount = accountMap.get(bidUserId);//从accountMap中获取到对象的账户
                        if (bidAccount == null) {
                            //如果账户为null的话 从accountService保存到accountMap中
                            bidAccount = accountService.get(bidUserId);
                            accountMap.put(bidUserId, bidAccount);
                        }
                        //账户的冻结金额减少 冻结金额 = 原本的冻结金额 - 该标的投标金额  TODO 待收本金 和 待收利息增加需要在还款对象 和 还款明细对象才能做 先做这个冻结金额先
                        bidAccount.setFreezedAmount(bidAccount.getFreezedAmount().subtract(bid.getAvailableAmount()));
                        //生成投标成功的流水
                        accountFlowService.createRechargeOfflineFlow(bidAccount, bid.getAvailableAmount(),
                                "成功投标,投标金额为: " + bid.getAvailableAmount() + "元",
                                Constants.ACCOUNT_ACTIONTYPE_BID_SUCCESSFUL);
                    } else {
                        //系统账户
                        expAccount = expAccountMap.get(bidUserId);
                        //也是需要把当前的用户放在accountMap中 后面需要用到它 非常重要
                        accountMap.put(bidUserId, this.accountService.get(bidUserId));
                        if (expAccount == null) {
                            expAccount = expAccountService.get(bidUserId);
                            expAccountMap.put(bidUserId, expAccount);
                        }
                        //账户的冻结金额减少 冻结金额 = 原本的冻结金额 - 该标的投标金额
                        expAccount.setFreezedAmount(expAccount.getFreezedAmount().subtract(bid.getAvailableAmount()));
                        //生成投标成功的流水
                        expAccountFlowService.createAuditSuccessFlow(expAccount, bid.getAvailableAmount());
                    }
                }

                Account bidUserAccountTemp;
                //生成还款对象 和 还款明细对象
                List<PaymentSchedule> paymentSchedules = createPaymentSchedules(bidRequest);
                for (PaymentSchedule ps : paymentSchedules) {
                    for (PaymentScheduleDetail psd : ps.getDetails()) {
                        //从map中获取到当前投资的账户
                        //如果是普通信用标 才需要本金 体验标是不需要本金的
                        bidUserAccountTemp = accountMap.get(psd.getInvestorId());
                        if (bidRequest.getBidRequestType() == Constants.BIDREQUEST_TYPE_NORMAL) {
                            //设置待收本金 = 原本的待收本金 + 新增的待收本金
                            bidUserAccountTemp.setUnReceivePrincipal(bidUserAccountTemp.getUnReceivePrincipal().add(psd.getPrincipal()));
                        }
                        //代收利息 = 原本的待收利息 + 新增的待收利息
                        bidUserAccountTemp.setUnReceiveInterest(bidUserAccountTemp.getUnReceiveInterest().add(psd.getInterest()));
                    }
                }

                //遍历accountMap 统一更新到数据库
                for (Account account : accountMap.values()) {
                    accountService.update(account);
                }

                //遍历expAccountMap 统一更新到数据库
                for (ExpAccount account : expAccountMap.values()) {
                    expAccountService.update(account);
                }

            } else {
                //如果审核拒绝 二审的拒绝 和 一审的拒绝是一样的 所有抽成一个方法
                auditReject(bidRequest);
            }
            this.update(bidRequest);
        }
    }

    /**
     * 根据借款对象生成还款对象
     *
     * @param bidRequest 借款对象
     * @return
     */
    private List<PaymentSchedule> createPaymentSchedules(BidRequest bidRequest) {
        List<PaymentSchedule> paymentSchedules = new ArrayList<>();
        //创建还款对象
        PaymentSchedule ps;
        //设置本期还款金额
        BigDecimal totalAmountTemp = Constants.ZERO;
        //设置本期还款利息
        BigDecimal interestTemp = Constants.ZERO;

        //创建几个还款对象 是看 还款周期决定的
        for (int i = 0; i < bidRequest.getMonthes2Return(); i++) {
            ps = new PaymentSchedule();
            ps.setBidRequestId(bidRequest.getId());//设置借款对象ID
            ps.setBidRequestTitle(bidRequest.getTitle());//设置借款标题
            ps.setBidRequestType(bidRequest.getBidRequestType());//设置借款类型
            ps.setBorrowUser(bidRequest.getCreateUser());//设置借款人
            ps.setMonthIndex(i + 1);//设置第几期还款
            ps.setReturnType(bidRequest.getReturnType());//设置还款方式
            //第N期还款的截止时间 = 发标时间 + (i + 1) 月
            ps.setDeadLine(DateUtils.addMonths(bidRequest.getPublishTime(), i + 1));//设置还款截止日期

            //判断是不是最后一期
            if (i < bidRequest.getMonthes2Return() - 1) {
                //如果不是最后一期
                //本期还款金额
                ps.setTotalAmount(CalculatetUtil.calMonthToReturnMoney(bidRequest.getReturnType(), bidRequest.getBidRequestAmount(),
                        bidRequest.getCurrentRate(), i + 1, bidRequest.getMonthes2Return()));

                //本期还款利息
                ps.setInterest(CalculatetUtil.calMonthlyInterest(bidRequest.getReturnType(), bidRequest.getBidRequestAmount(),
                        bidRequest.getCurrentRate(), i + 1, bidRequest.getMonthes2Return()));

                //本期还款本金 还款金额 - 还款利息
                ps.setPrincipal(ps.getTotalAmount().subtract(ps.getInterest()));

                //设置 totalAmountTemp 一个变量叠加起来 以供于最后一期 计算出最后的还款金额
                totalAmountTemp = totalAmountTemp.add(ps.getTotalAmount());

                //设置 interestTemp 一个变量叠加起来 以供于最后一期 计算出最后的还款利息
                interestTemp = interestTemp.add(ps.getInterest());

            } else {
                //如果是最后一期 还款金额 = 借款总金额 + 借款总利息 - 前几期加起来的还款金额 totalAmountTemp
                ps.setTotalAmount(bidRequest.getBidRequestAmount().add(bidRequest.getTotalRewardAmount()).subtract(totalAmountTemp));

                //还款利息 =  借款总利息 - 前几期加起来的还款利息 interestTemp
                ps.setInterest(bidRequest.getTotalRewardAmount().subtract(interestTemp));

                //最后还款本金 = 最后还款金额 - 最后还款利息
                ps.setPrincipal(ps.getTotalAmount().subtract(ps.getInterest()));
            }


            //保存还款对象到数据库
            paymentScheduleService.save(ps);

            ps.setDetails(createPaymentSchedulesDetails(bidRequest, ps));

            //添加到集合中
            paymentSchedules.add(ps);
        }
        return paymentSchedules;
    }

    /**
     * 创建还款计划明细
     *
     * @param bidRequest 借款对象
     * @param ps         还款对象
     * @return
     */
    private List<PaymentScheduleDetail> createPaymentSchedulesDetails(BidRequest bidRequest, PaymentSchedule ps) {
        List<PaymentScheduleDetail> paymentScheduleDetails = new ArrayList<>();
        //创建还款计划明细对象
        PaymentScheduleDetail psd;
        //设置本期 本金还款金额
        BigDecimal principalTemp = Constants.ZERO;
        //设置本期还款利息
        BigDecimal interestTemp = Constants.ZERO;
        //设置投标人对象
        Bid bid;

        //创建几个还款计划明细对象 是看 投标人数量决定的
        for (int i = 0; i < bidRequest.getBids().size(); i++) {
            //设置投标人对象
            bid = bidRequest.getBids().get(i);
            //创建还款计划明细对象
            psd = new PaymentScheduleDetail();

            psd.setBidRequestId(bidRequest.getId());//设置借款对象
            psd.setReturnType(bidRequest.getReturnType());//设置还款方式
            psd.setDeadLine(ps.getDeadLine());//设置本期还款截止日期
            psd.setMonthIndex(ps.getMonthIndex());//设置第几期还款
            psd.setBidAmount(bid.getAvailableAmount());//设置总共投标金额
            psd.setBidId(bid.getId());//设置投标对象
            psd.setPaymentScheduleId(ps.getId());//所属还款计划
            psd.setBorrowUser(ps.getBorrowUser());//设置发标人
            psd.setInvestorId(bid.getBidUser().getId()); //设置投资人(收款人)

            //判断不是最后一期
            if (i < bidRequest.getBids().size() - 1) {
                //当前的投标金额 / 借款金额 得到一个比例出来
                BigDecimal rate = bid.getAvailableAmount().divide(bidRequest.getBidRequestAmount())
                        .setScale(Constants.SCALE_CAL, RoundingMode.HALF_UP);
                //该还款的明细本金 = 该期的还款本金 * 上面的比例
                psd.setPrincipal(ps.getPrincipal().multiply(rate)
                        .setScale(Constants.SCALE_STORE, RoundingMode.HALF_UP));
                //该还款的明细利息 = 该期的还款利息 * 上面的比例
                psd.setInterest(ps.getInterest().multiply(rate)
                        .setScale(Constants.SCALE_STORE, RoundingMode.HALF_UP));
                //该还款的本息 = 该还款的明细本金 + 该还款的明细利息
                psd.setTotalAmount(psd.getPrincipal().add(psd.getInterest()));

                //设置 totalAmountTemp 一个变量叠加起来 以供于最后一期 计算出最后的还款金额
                principalTemp = principalTemp.add(psd.getPrincipal());

                //设置 interestTemp 一个变量叠加起来 以供于最后一期 计算出最后的还款利息
                interestTemp = interestTemp.add(psd.getInterest());

            } else {
                //最后一期的本金还款
                psd.setPrincipal(ps.getPrincipal().subtract(principalTemp));
                //最后一期的利息还款
                psd.setInterest(ps.getInterest().subtract(interestTemp));
                //最后一期 还款本息
                psd.setTotalAmount(psd.getPrincipal().add(psd.getInterest()));
            }
            //保存到数据库
            paymentScheduleDetailService.save(psd);
            //添加到集合中
            paymentScheduleDetails.add(psd);
        }
        return paymentScheduleDetails;
    }
}
